﻿// <copyright file="RotateTransform3D.cs" company="Axelerate Solutions S.A.">
// Copyright (c) Axelerate Solutions S.A.  All rights reserved.
// </copyright>

namespace Axelerate.Silverlight3D.Media.Media3D
{
    using System;
    using System.Net;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Documents;
    using System.Windows.Ink;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Animation;
    using System.Windows.Shapes;

    
    /// <summary>
    /// File name: RotateTransform3D.cs
    /// Company: Axelerate Solutions S.A.
    /// Created By: Daniel Rojas
    /// Created Date: 10/10/2008 10:57:00 h.
    /// Description: Specifies a rotation transformation.
    ///   <Modifications>
    ///   </Modifications>
    /// </summary>
    public sealed class RotateTransform3D : AffineTransform3D
    {
        #region Fields

        /// <summary>
        /// Identifies the CenterX dependency property.
        /// </summary>
        public static readonly DependencyProperty CenterXProperty =
                    DependencyProperty.Register(
                          "CenterX",
                          typeof(double),
                          typeof(RotateTransform3D),
                          new PropertyMetadata(OnCenterXPropertyChanged));

        /// <summary>
        /// Identifies the CenterY dependency property.
        /// </summary>
        public static readonly DependencyProperty CenterYProperty =
                    DependencyProperty.Register(
                          "CenterY",
                          typeof(double),
                          typeof(RotateTransform3D),
                          new PropertyMetadata(OnCenterYPropertyChanged));

        /// <summary>
        /// public static readonly DependencyProperty CenterZProperty
        /// </summary>
        public static readonly DependencyProperty CenterZProperty =
                    DependencyProperty.Register(
                          "CenterZ",
                          typeof(double),
                          typeof(RotateTransform3D),
                          new PropertyMetadata(OnCenterZPropertyChanged));

        /// <summary>
        /// Identifies the Rotation dependency property.
        /// </summary>
        public static readonly DependencyProperty RotationProperty =
                    DependencyProperty.Register(
                          "Rotation",
                          typeof(Rotation3D),
                          typeof(RotateTransform3D),
                          new PropertyMetadata(OnRotationPropertyChanged));

        #endregion

        #region Properties
        
        /// <summary>
        /// Gets or sets the X coordinate of the Point3D about which to rotate. This is a dependency property.
        /// </summary>
        public double CenterX
        {
            get
            {

                return (double)this.GetValue(CenterXProperty);
            }
            set
            {
                this.SetValue(CenterXProperty, value);
            }
        }

        /// <summary>
        /// Gets or sets the Y coordinate of the Point3D about which to rotate. This is a dependency property.
        /// </summary>
        public double CenterY
        {
            get
            {

                return (double)this.GetValue(CenterYProperty);
            }
            set
            {
                this.SetValue(CenterYProperty, value);
            }
        }

        /// <summary>
        /// Gets or sets the Z coordinate of the Point3D about which to rotate. This is a dependency property.
        /// </summary>
        public double CenterZ
        {
            get
            {

                return (double)this.GetValue(CenterZProperty);
            }
            set
            {
                this.SetValue(CenterZProperty, value);
            }
        }

        /// <summary>
        /// Gets or sets a Rotation3D that specifies the rotation. This is a dependency property.
        /// </summary>
        public Rotation3D Rotation
        {
            get
            {
                return (Rotation3D)this.GetValue(RotationProperty);
            }
            set
            {
                this.SetValue(RotationProperty, value);
                value.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(RotationValue_PropertyChanged);
            }
        }

        /// <summary>
        /// Notify that the Rotation property or one of his properties changed.
        /// </summary>
        /// <param name="sender">object that send the event</param>
        /// <param name="e">PropertyChangedEventArgs with the information of what property changed and how.</param>
        void RotationValue_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            this.NotifyPropertyChanged("Rotation");
        }

        #endregion

        #region Constructor
       
        /// <summary>
        /// Initializes a new instance of the RotateTransform3D class.
        /// </summary>
        public RotateTransform3D():base()
        {
        }

        /// <summary>
        /// Initializes a new instance of the RotateTransform3D class with the specified rotation.
        /// </summary>
        /// <param name="rotation">Rotation3D that specifies the rotation.</param>
        public RotateTransform3D(Rotation3D rotation)
        {
            this.Rotation = rotation;
        }

        /// <summary>
        /// Initializes a new instance of the RotateTransform3D class with the specified center and rotation.
        /// </summary>
        /// <param name="rotation">Rotation3D that specifies the rotation.</param>
        /// <param name="center">Center of the transformation's rotation.</param>
        public RotateTransform3D(Rotation3D rotation, Point3D center)
        {
            this.Rotation = rotation;
            this.CenterX = center.X;
            this.CenterY = center.Y;
            this.CenterZ = center.Z;
        }

        /// <summary>
        /// Initializes a new instance of the RotateTransform3D class uusing the specified rotation and center coordinates.
        /// </summary>
        /// <param name="rotation">Rotation3D that specifies the rotation.</param>
        /// <param name="centerX">Double that specifies the X value about which to rotate.</param>
        /// <param name="centerY">Double that specifies the Y value about which to rotate.</param>
        /// <param name="centerZ">Double that specifies the Z value about which to rotate.</param>
        public RotateTransform3D(Rotation3D rotation, double centerX, double centerY, double centerZ)
        {
            this.Rotation = rotation;
            this.CenterX = centerX;
            this.CenterY = centerY;
            this.CenterZ = centerZ;
        }

        #endregion

        #region Methods
        /// <summary>
        /// Raise the CenterX changed event
        /// </summary>
        /// <param name="d">DependencyObject owner of the property</param>
        /// <param name="e">DependencyPropertyChangedEventArgs with the necesary information</param>
        private static void OnCenterXPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((RotateTransform3D)d).NotifyPropertyChanged("CenterX");
        }

        /// <summary>
        /// Raise the CenterY changed event
        /// </summary>
        /// <param name="d">DependencyObject owner of the property</param>
        /// <param name="e">DependencyPropertyChangedEventArgs with the necesary information</param>
        private static void OnCenterYPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((RotateTransform3D)d).NotifyPropertyChanged("CenterY");
        }

        /// <summary>
        /// Raise the CenterZ changed event
        /// </summary>
        /// <param name="d">DependencyObject owner of the property</param>
        /// <param name="e">DependencyPropertyChangedEventArgs with the necesary information</param>
        private static void OnCenterZPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((RotateTransform3D)d).NotifyPropertyChanged("CenterZ");
        }

        /// <summary>
        /// Raise the Rotation changed event
        /// </summary>
        /// <param name="d">DependencyObject owner of the property</param>
        /// <param name="e">DependencyPropertyChangedEventArgs with the necesary information</param>
        private static void OnRotationPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((RotateTransform3D)d).NotifyPropertyChanged("Rotation");
        }
        #endregion

        /// <summary>
        /// Retrieves a Matrix3D that represents the rotation.
        /// </summary>
        public override Matrix3D Value
        { 
            // TODO: review this method for optimization
            get
            {
                Matrix3D result = Matrix3D.Identity;
                if (Rotation != null)
                {
                    Quaternion q = Rotation.InternalQuaternion;
                    double yy = 2 * q.Y * q.Y;
                    double zz = 2 * q.Z * q.Z;
                    double xx = 2 * q.X * q.X;
                    double xy = 2 * q.X * q.Y;
                    double zw = 2 * q.W * q.Z;
                    double xz = 2 * q.X * q.Z;
                    double yw = 2 * q.W * q.Y;
                    double yz = 2 * q.Y * q.Z;
                    double xw = 2 * q.W * q.X;
                    
                    result.M11 = 1 - yy - zz;
                    result.M12 = xy + zw;
                    result.M13 = xz - yw;
                    result.M14 = 0;

                    result.M21 = xy - zw;
                    result.M22 = 1 - xx - zz;
                    result.M23 = yz + xw; // check this!!!
                    result.M24 = 0;

                    result.M31 = xz + yw;
                    result.M32 = yz - xw;
                    result.M33 = 1 - xx - yy;
                    result.M34 = 0;

                    result.M44 = 1;

                    if ((this.CenterX != 0) || (this.CenterY != 0) || (this.CenterZ != 0))
                    {
                        result.OffsetX = (((-this.CenterX * result.M11) -
                                           (this.CenterY * result.M21)) -
                                           (this.CenterZ * result.M31)) + this.CenterX;

                        result.OffsetY = (((-this.CenterX * result.M12) -
                                           (this.CenterY * result.M22)) -
                                           (this.CenterZ * result.M32)) + this.CenterY;

                        result.OffsetZ = (((-this.CenterX * result.M13) -
                                           (this.CenterY * result.M23)) -
                                           (this.CenterZ * result.M33)) + this.CenterZ;
                    }
                }
                return result;
            }
        }
    }
}
